/*
 * @(#)LdapImportManager.java
 *
 * Copyright (c) 2003 DCIVision Ltd
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of DCIVision
 * Ltd ("Confidential Information").  You shall not disclose such Confidential
 * Information and shall use it only in accordance with the terms of the license
 * agreement you entered into with DCIVision Ltd.
 */
package com.dcivision.ldap.core;

/**
 * LdapImportManager.java
 * 
 * This class is responsible import users from Ldap .
 * 
 * @author Charlie.Liu
 * @company DCIVision Limited
 * @creation date 17/12/2004
 * @version $Revision: 1.4.2.10 $
 * 
 */
import java.io.UnsupportedEncodingException;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

import javax.mail.internet.AddressException;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.SearchResult;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.dcivision.framework.ApplicationException;
import com.dcivision.framework.GlobalConstant;
import com.dcivision.framework.MessageResourcesFactory;
import com.dcivision.framework.SessionContainer;
import com.dcivision.framework.SystemParameterConstant;
import com.dcivision.framework.SystemParameterFactory;
import com.dcivision.framework.Utility;
import com.dcivision.framework.bean.GenericAddress;
import com.dcivision.framework.notification.EmailDeliveryMessage;
import com.dcivision.framework.notification.NotificationWay;
import com.dcivision.framework.notification.NotificationWayFactory;
import com.dcivision.ldap.bean.LdapImportResult;
import com.dcivision.staff.bean.StaffRecord;
import com.dcivision.staff.dao.StaffRecordDAObject;
import com.dcivision.user.bean.MtmUserRecordUserGroup;
import com.dcivision.user.bean.UserGroup;
import com.dcivision.user.bean.UserRecord;
import com.dcivision.user.dao.MtmUserRecordUserGroupDAObject;
import com.dcivision.user.dao.UserGroupDAObject;
import com.dcivision.user.dao.UserRecordDAObject;

public class LdapImportManager {

  private Log logger = null;
  private Env env = null;
  private Connection conn = null;
  SessionContainer sessionContainer = null;

  /**
   * Constructor
   * 
   * @param importForm
   */

  public LdapImportManager(SessionContainer ctx, Connection conn,Env env) {
    this.env = env;
    this.conn = conn;
    this.sessionContainer = ctx;
    this.logger = LogFactory.getLog(this.getClass().getName());
  }

  /**
   * importData - import users from Ldap
   * Ldap Import Flow
   * 1) create group
   * 2) create staff
   * 3) create user
   * 4) base on user's memberof information to map back to user group   
   * @param importForm
   * @return
   */
  public List importData(boolean createNewUserFlag, boolean createUserGroupFlag) throws LdapImportException{
    logger.info("Ldap ImportData start!");        
    List result = new ArrayList();
    try {
      if(createUserGroupFlag){
        importUserGroup();
      }
      result =  importUserAndStaff(createNewUserFlag);            
      
      conn.commit();
    } catch (Exception e) { 
      logger.error(e);
      throw new LdapImportException(e);
    }
    return result;
  }
  
  public List importData(boolean createNewUserFlag, boolean createUserGroupFlag, boolean withNotification) throws ApplicationException{
    List results = this.importData(createNewUserFlag,createUserGroupFlag);
    if(withNotification){
      String adminEmailAddress = SystemParameterFactory.getSystemParameter(SystemParameterConstant.SMTP_ADMIN_ADDRESS);
      String adminUserName     = SystemParameterFactory.getSystemParameter(SystemParameterConstant.SMTP_ADMIN_NAME);
      sendEmailNotification(results,adminEmailAddress,adminUserName);
    }
    return results;
  } 
  
  protected String formatEmailNotificationContent(List results){
    StringBuffer msgBuffer = new StringBuffer();
    StringBuffer errorMsgBuffer = new StringBuffer();
    int successCount = 0;
    int errorCount = 0;
    for(int i=0;i<results.size();i++){
      LdapImportResult result = (LdapImportResult)results.get(i);
      if(result.getExceptionList().size()>0){
        LdapImportException ex = (LdapImportException)result.getExceptionList().get(0);
        if(ex instanceof AttributeMissingException){
          String distinguishedName = result.getLdapName();
          String missingAttributeKey = ((AttributeMissingException)ex).getMissingAttributeKey();                    
          errorMsgBuffer.append(MessageResourcesFactory.getMessage(sessionContainer.getSessionLocale(),"user.label.ldap_import_notification_missing_attribute",distinguishedName,missingAttributeKey));          
          errorMsgBuffer.append("<br>");
        }        
      }else{
        successCount++;
      }
      errorCount = results.size() - successCount;
    }
    String now = Utility.getCurrentTimestamp().toString();    
    String[] args = {now ,String.valueOf(successCount),String.valueOf(errorCount)};
    msgBuffer.append(MessageResourcesFactory.getMessage(sessionContainer.getSessionLocale(),"user.label.ldap_import_notification_content", args));          
    msgBuffer.append(MessageResourcesFactory.getMessage(sessionContainer.getSessionLocale(),"user.label.ldap_import_notification_separator"));          
    msgBuffer.append(errorMsgBuffer);          
    return msgBuffer.toString();
  }
  
  protected void sendEmailNotification(List results, String targetUserEmailAddress,String receiverName) throws ApplicationException{      
    try 
    {
      String content = formatEmailNotificationContent(results);
      EmailDeliveryMessage msg = new EmailDeliveryMessage();
      msg.setSender(new GenericAddress(targetUserEmailAddress,receiverName));
      msg.addToRecipient(new GenericAddress(targetUserEmailAddress));
      msg.setSubject(MessageResourcesFactory.getMessage(sessionContainer.getSessionLocale(),"user.label.ldap_import_notification_subject"));
      msg.setMailContent(content.toString());
      msg.setContentType(EmailDeliveryMessage.CONTENT_TYPE_HTML);
      NotificationWay emailNotification = NotificationWayFactory.getNotificationInstance(NotificationWay.EMAIL_NOTIFICATION_INSTANCE);
      emailNotification.addDeliveryMessage( msg);
      emailNotification.send();
    } catch (AddressException ae) {
      throw new ApplicationException();
    } catch (UnsupportedEncodingException uee) {
      throw new ApplicationException();
    }
  }      
  
  //validate staff is good
  private void validateStaff(StaffRecord staff) throws AttributeMissingException{
     logger.debug(staff);
    if(staff.getStaffCode()==null){
      throw new AttributeMissingException("error.user.ldap_import_missing.staff_code");
    }
    if(staff.getFirstName()==null){
      throw new AttributeMissingException("error.user.ldap_import_missing_first_name");
    }
    if(staff.getLastName()==null){
      throw new AttributeMissingException("error.user.ldap_import_missing_last_name");
    }
    if(staff.getFullName()==null){
      throw new AttributeMissingException("error.user.ldap_import_missing_full_name");
    }
  }
  
  //validate user is good
  private void validateUser(UserRecord user) throws AttributeMissingException{
    logger.debug(user);
    if(user.getFirstName()==null){
      throw new AttributeMissingException("error.user.ldap_import_missing_first_name");
    }
    if(user.getLastName()==null){
      throw new AttributeMissingException("error.user.ldap_import_missing_last_name");
    }
    if(user.getFullName()==null){
      throw new AttributeMissingException("error.user.ldap_import_missing_full_name");
    }
    if(user.getLoginName()==null){
      throw new AttributeMissingException("error.user.ldap_import_missing_login_name");
    }  
  }

  

  public List importUserAndStaff(boolean createNewUserFlag) throws ApplicationException{
    List results = new ArrayList();
    try {    
      UserRecordDAObject userRecordDAO = new UserRecordDAObject(sessionContainer,conn);
      StaffRecordDAObject staffRecordDAO = new StaffRecordDAObject(sessionContainer, conn);      
      UserGroupDAObject userGroupDAO = new UserGroupDAObject(sessionContainer,conn);      
      MtmUserRecordUserGroupDAObject mtmUserRecordUserGroupDAO = new MtmUserRecordUserGroupDAObject(sessionContainer,conn);      
    
      //specify the Ldap search filter
      String searchFilter = "objectClass=user";
    
      //Create the initial directory context
      LdapMethod ldapMethod = new LdapMethod();
      Env env = new Env();
      env.setFilter(searchFilter);            
      NamingEnumeration ldapSearchResults = ldapMethod.search(env);        
      while (ldapSearchResults.hasMoreElements()) {        
        SearchResult sr = (SearchResult) ldapSearchResults.next();
        Attributes attrs = sr.getAttributes();
        if (attrs != null) {
          String userName = null; 
          String distinguishedName = sr.getName() + "," + env.getSearchbase();
          
          for (NamingEnumeration ae = attrs.getAll(); ae.hasMore(); ) {
            Attribute attr = (Attribute) ae.next();                                        
            if (attr.getID().equalsIgnoreCase(env.getLoginNameMapping())){                                          
              userName = attr.getAll().nextElement().toString();
              break;
            }
          }
          LdapImportResult importResult = new LdapImportResult();
          try{
            if(!Utility.isEmpty(userName)){
              UserRecord user = (UserRecord)userRecordDAO.getByLogin(userName);
              StaffRecord staff = null;              
              if (user == null) {
                if(createNewUserFlag){
                  staff = new StaffRecord();
                  user  = new UserRecord();
                  
                  List existStaffRecords = staffRecordDAO.getStaffRecordListByStaffCode(staff.getStaffCode());
                  if(existStaffRecords.size()==0){
                    parseLdapResultToStaffRecord(staff,sr);     
                    staff = (StaffRecord)staffRecordDAO.insertObject(staff);
                  }else{
                    staff = (StaffRecord)existStaffRecords.get(0);
                    parseLdapResultToStaffRecord(staff,sr);     
                    staff = (StaffRecord)staffRecordDAO.updateObject(staff);
                  }                              
                  user = parseLdapResultToUserRecord(user,sr);    
                  user.setUserLinkID(staff.getID());
                  user =(UserRecord)userRecordDAO.insertObject(user);                                
                  importResult.setCreateAccountResult("user.label.ldap_import_result_created");
                  importResult.setCreateStaffResult("user.label.ldap_import_result_created");
                  logger.debug("account has been created!");
                }else{
                  importResult.setCreateAccountResult("user.label.ldap_import_result_ignored");
                  importResult.setCreateStaffResult("user.label.ldap_import_result_ignored");  
                }
              } else {                  
                try{
                  staff = (StaffRecord)staffRecordDAO.getObjectByID(user.getUserLinkID());
                }catch(ApplicationException ee){
                  logger.error("staff record not found.");
                }
                if(!GlobalConstant.STATUS_ACTIVE.equals(user.getStatus())){
                  user = parseLdapResultToUserRecord(user,sr);    
                  user.setLoginPwd("");              
                  user =(UserRecord)userRecordDAO.updateObject(user);                                                                        
                  if (staff == null) {                                            
                    parseLdapResultToStaffRecord(staff,sr);
                    staff = (StaffRecord)staffRecordDAO.insertObject(staff);                  
                    importResult.setCreateStaffResult("user.label.ldap_import_result_created");
                  } else {                    
                    parseLdapResultToStaffRecord(staff,sr);
                    staff = (StaffRecord)staffRecordDAO.updateObject(staff);
                    importResult.setCreateStaffResult("user.label.ldap_import_result_duplicate");
                  }                                                    
                  importResult.setCreateAccountResult("user.label.ldap_import_result_re-actived");
                }else{
                  importResult.setCreateAccountResult("user.label.ldap_import_result_duplicate");
                  importResult.setCreateStaffResult("user.label.ldap_import_result_duplicate");
                }                
              }                
              //Only update user group membership information if user record is not null
              //user record can be null if createNewUserFlag = false and the ldap entry doesn't exists in ParaDM
              if(user!=null){
                mtmUserRecordUserGroupDAO.deleteListByUserRecordID(user.getID());
                List memberships = parseLdapResultToUserGroupMembership(sr);              
                for(int j=0;j<memberships.size();j++){
                  String groupName = memberships.get(j).toString();
                  UserGroup userGroup = (UserGroup)userGroupDAO.getObjectByGroupName(groupName);
                  if(userGroup!=null){
                    MtmUserRecordUserGroup mtmUserRecordUserGroup = new MtmUserRecordUserGroup();
                    mtmUserRecordUserGroup.setUserRecordID(user.getID());
                    mtmUserRecordUserGroup.setUserGroupID(userGroup.getID());                        
                    mtmUserRecordUserGroupDAO.insertObject(mtmUserRecordUserGroup);
                  }
                }                
              }
              importResult.setStaff(staff);
              importResult.setLdapName(distinguishedName);              
              results.add(importResult);            
            }
          }catch(AttributeMissingException e){
            logger.error("Missing attribute:"+e.getMissingAttributeKey());
            importResult.setLdapName(distinguishedName); 
            importResult.addException(e);
            results.add(importResult);            
          }           
        }       
      }         
    }catch (NamingException e) {    
      logger.error(e, e);
      throw new LdapImportException(e);      
    }   
    return results;
  }  
  
  public List parseLdapResultToUserGroupMembership(SearchResult searchResult) throws ApplicationException{
    List memberships = new ArrayList();
    Attributes attrs = searchResult.getAttributes();
    try{
      if (attrs == null) {
        logger.debug("No attributes");
      } else {
        // get mapping attribute's value
        for (NamingEnumeration ae = attrs.getAll(); ae.hasMoreElements();) {
          Attribute attr = (Attribute) ae.next();
          String attrId = attr.getID();
          
          if (attrId.equalsIgnoreCase("memberOf")) { 
            for (NamingEnumeration groupNames = attr.getAll(); groupNames.hasMoreElements();) {
              String userGroupName = groupNames.nextElement().toString();
              if(userGroupName.indexOf(",")<0){
                userGroupName = userGroupName.substring(userGroupName.indexOf("=")+1);  
              }else{
                userGroupName = userGroupName.substring(userGroupName.indexOf("=")+1,userGroupName.indexOf(","));
              }            
              System.out.println("Member Of:"+userGroupName);            
              memberships.add(userGroupName);
            }
            break;
          }
        }
      }
    }catch (NamingException e) {
      throw new LdapImportException(e);      
    }     
    return memberships;
  }  
  
  public UserRecord parseLdapResultToUserRecord(UserRecord user,SearchResult searchResult) throws ApplicationException{         
    //SearchResult si = (SearchResult) results.next();          
    //UserRecord user = new UserRecord();
    //Env env = new Env();
    Attributes attrs = searchResult.getAttributes();
    try{
      if (attrs == null) {
        logger.debug("No attributes");
      } else {
        // get mapping attribute's value
        for (NamingEnumeration ae = attrs.getAll(); ae.hasMoreElements();) {
          Attribute attr = (Attribute) ae.next();
          String attrId = attr.getID();
          //logger.debug("Attribute:"+attrId);
          if ((env.getMappingString().indexOf(attrId)) < 0) {
            continue;
          }
          String value = "";
          for (Enumeration vals = attr.getAll(); vals.hasMoreElements();) {
            if (value.equals("")) {
              value = vals.nextElement().toString();
            } else {
              value += "," + vals.nextElement().toString();
            }
          }
          // set fistName
          if (attrId.equalsIgnoreCase(env.getFirstNameMapping())) {
            user.setFirstName(value);
          }// set lastName
          if (attrId.equalsIgnoreCase(env.getLastNameMapping())) {
            user.setLastName(value);
          }// set fullNanme
          if (attrId.equalsIgnoreCase(env.getFullNameMapping())) {
            user.setFullName(value);
          }// set email
          if (attrId.equalsIgnoreCase(env.getEmailMapping())) {
            user.setEmail(value);
          }// set loginName
          if (attrId.equalsIgnoreCase(env.getLoginNameMapping())){
            user.setLoginName(value);                
          }
          // set password
        }
        user.setModifyLoginPwd("N");
        user.setStatus(GlobalConstant.STATUS_ACTIVE);
        user.setRecordStatus(GlobalConstant.RECORD_STATUS_ACTIVE);
        /*
        if(null==user.getLoginPwd()){
          user.setLoginPwd("password");
        }
        */
        //validate user  
        if(Utility.isEmpty(user.getFullName())){
          user.setFullName(user.getFirstName() + " " +user.getLastName());  
        }
        validateUser(user);            
      }      
    }catch (NamingException e) {
      logger.error(e, e);
      throw new LdapImportException(e);      
    }     
    return user;
  }
  
  public StaffRecord parseLdapResultToStaffRecord(StaffRecord staff,SearchResult searchResult) throws ApplicationException{
    //StaffRecord staff = new StaffRecord();
    Attributes attrs = searchResult.getAttributes();
    try{
      if (attrs == null) {
        logger.debug("No attributes");
      } else {
        // get mapping attribute's value
        for (NamingEnumeration ae = attrs.getAll(); ae.hasMoreElements();) {
          Attribute attr = (Attribute) ae.next();
          String attrId = attr.getID();
          logger.debug("Attribute:"+attrId);
          if ((env.getMappingString().indexOf(attrId)) < 0) {
            continue;
          }
          String value = "";
          for (Enumeration vals = attr.getAll(); vals.hasMoreElements();) {
            if (value.equals("")) {
              value = vals.nextElement().toString();
            } else {
              value += "," + vals.nextElement().toString();
            }
          }// set staffCode
          if (attrId.equalsIgnoreCase(env.getStaffCodeMapping())) {          
            staff.setStaffCode(value);
          }// set fistName
          if (attrId.equalsIgnoreCase(env.getFirstNameMapping())) {          
            staff.setFirstName(value);
          }// set lastName
          if (attrId.equalsIgnoreCase(env.getLastNameMapping())) {        
            staff.setLastName(value);          
          }// set fullNanme
          if (attrId.equalsIgnoreCase(env.getFullNameMapping())) {          
            staff.setFullName(value);          
          }// set email
          if (attrId.equalsIgnoreCase(env.getEmailMapping())) {
            logger.debug("Email=" + value);
            staff.setEmail(value);
          }// set loginName
        }
        staff.setSalutationID(new Integer(1));
        staff.setGender("M");
        staff.setStatus(GlobalConstant.STATUS_ACTIVE);            
        staff.setRecordStatus(GlobalConstant.RECORD_STATUS_ACTIVE);
        //validate staff 
        if(Utility.isEmpty(staff.getFullName())){
          staff.setFullName(staff.getFirstName() + " " + staff.getLastName());  
        }
        if(Utility.isEmpty(staff.getStaffCode())){
          staff.setStaffCode("N/A");  
        }            
        validateStaff(staff);
      }
    }catch (NamingException e) {
      logger.error(e, e);
      throw new LdapImportException(e);      
    }     
    return staff;
  }  
  
  public List importUserGroup() throws ApplicationException{
    List newGroupImported = new ArrayList();
    
    try {
      UserGroupDAObject userGroupDAO = new UserGroupDAObject(sessionContainer,conn);
      //specify the Ldap search filter
      String searchFilter = "objectClass=group";
    
      //Create the initial directory context
      LdapMethod ldapMethod = new LdapMethod();
      //Env env = new Env();
      env.setFilter(searchFilter);            
      NamingEnumeration results = ldapMethod.search(env);  
      while (results.hasMoreElements()) {
        SearchResult sr = (SearchResult) results.next();
        Attributes attrs = sr.getAttributes();
        if (attrs != null) {
          String groupName = null;
          for (NamingEnumeration ae = attrs.getAll(); ae.hasMore(); ) {
            Attribute attr = (Attribute) ae.next();               
            if("cn".equalsIgnoreCase(attr.getID())){                            
              groupName = attr.getAll().nextElement().toString();
              break;
            }
          }
          UserGroup group = (UserGroup)userGroupDAO.getObjectByGroupName(groupName);            
          if(group == null){
            group = new UserGroup();
            groupName = groupName.trim();
            group.setGroupName(groupName);
            group.setOwnerUserRecordID(sessionContainer.getUserRecord().getID());
            group.setGroupType(UserGroup.GROUP_TYPE_PUBLIC);            
            group=(UserGroup)userGroupDAO.insertObject(group);              
            newGroupImported.add(group);
          }            
        }
      }
    }catch (NamingException e) {  
      throw new LdapImportException(e);      
    }   
    return newGroupImported;
  }
  
}
